#include "xdg_view.h"
#include "wlroots-full.hpp"
#include "view.h"

XdgView::XdgView(wlr_xdg_surface *xdg_surface, Server *server)
    // : m_server(server)
    : m_xdg_surface(xdg_surface)
{
    m_base = new View(m_xdg_surface->surface, BWL_XDG_SHELL_VIEW, this, server);
#if 1

#if 1
    struct wlr_scene_tree *tree = wlr_scene_xdg_surface_create(m_base->m_scene_tree, xdg_surface);
    if (!tree) {
        /* TODO: might need further clean up */
        wl_resource_post_no_memory(xdg_surface->resource);
        // free(xdg_toplevel_view);
        exit(1); //TODO
    }
    m_base->m_scene_node = &tree->node;

#else
    wlr_xdg_toplevel_set_size


#endif
    // node_descriptor_create(&view->scene_tree->node,
    // LAB_NODE_DESC_VIEW, view);

    /*
     * The xdg_toplevel_decoration and kde_server_decoration protocols
     * expects clients to use client side decorations unless server side
     * decorations are negotiated. So we default to client side ones here.
     *
     * TODO: We may want to assign the default based on a new rc.xml
     *       config option like "enforce-server" in the future.
     */
    // view->ssd_preference = LAB_SSD_PREF_CLIENT;

    /*
     * xdg_toplevel_decoration and kde_server_decoration use this
     * pointer to connect the view to a decoration object that may
     * be created in the future.
     */
    m_xdg_surface->data = m_base;

    /*
     * GTK4 initializes the decorations on the wl_surface before
     * converting it into a xdg surface. This call takes care of
     * connecting the view to an existing decoration. If there
     * is no existing decoration object available for the
     * wl_surface, this call is a no-op.
     */
    // kde_server_decoration_set_view(view, xdg_surface->surface);

    /* In support of xdg popups */
    m_xdg_surface->surface->data = tree;

#endif
#if 0
    /* Events specific to XDG toplevel views */
    CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_app_id);
    CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup);

    wl_list_insert(&server->views, &view->link);
#else
    m_base->m_destroy.connect(&m_xdg_surface->events.destroy, [=](void* data) {
        destroy();
    });

    m_base->m_request_move.connect(&m_xdg_surface->toplevel->events.request_move, [=](void* data) {
        request_move();
    });

    m_base->m_request_resize.connect(&m_xdg_surface->toplevel->events.request_resize, [=](void* data) {
        struct wlr_xdg_toplevel_resize_event *event = (struct wlr_xdg_toplevel_resize_event*)data;
        request_resize(event);
    });

    m_base->m_request_maximize.connect(&m_xdg_surface->toplevel->events.request_maximize, [=](void* data) {
        request_maximize();
    });

    m_base->m_request_minimize.connect(&m_xdg_surface->toplevel->events.request_minimize, [=](void* data) {
        request_minimize();
    });

    m_base->m_request_fullscreen.connect(&m_xdg_surface->toplevel->events.request_fullscreen, [=](void* data) {
        request_fullscreen();
    });

    m_base->m_set_title.connect(&m_xdg_surface->toplevel->events.set_title, [=](void* data) {
        set_title();
    });

    m_set_app_id.connect(&m_xdg_surface->toplevel->events.set_app_id, [=](void* data) {
        set_app_id();
    });

    m_new_popup.connect(&m_xdg_surface->events.new_popup, [=](void* data) {
        new_popup();
    });
#endif

}

void XdgView::set_output(OutputWrapper *output)
{
    m_base->set_output(output);
}

void XdgView::configure(wlr_box geo)
{

}

void XdgView::close()
{

}

const char *XdgView::get_string_prop(const char *prop)
{

    return "";
}

void XdgView::map()
{
    /*
     * An output should have been chosen when the surface was first
     * created, but take one more opportunity to assign an output if not.
     */
    if (!m_base->m_output) {
        m_base->set_output(m_base->m_server->output_nearest_to_cursor());
    }
    // struct wlr_xdg_surface *xdg_surface = xdg_surface_from_base(view);
    // m_base->m_surface = m_xdg_surface->surface;
    wlr_scene_node_set_enabled(&m_base->m_scene_tree->node, true);
    // return;
#if 0
    if (!m_base->m_been_mapped) {
        struct wlr_xdg_toplevel_requested *requested = &m_xdg_surface->toplevel->requested;
        // init_foreign_toplevel();
    }
#endif

    m_base->m_commit.connect(&m_xdg_surface->surface->events.commit, [=](void *data) {
        commit();
    });
}

void XdgView::set_activated(struct wlr_surface *surface, bool activated)
{
    if (!surface)
        return;
    struct wlr_xdg_surface *previous = wlr_xdg_surface_try_from_wlr_surface(surface);
    wlr_xdg_toplevel_set_activated(previous->toplevel, activated);
}

void XdgView::set_fullscreen(bool fullscreen)
{

}

void XdgView::notify_tiled()
{

}

void XdgView::unmap(bool client_request)
{

}

void XdgView::maximize(bool maximize)
{

}

void XdgView::minimize(bool minimize)
{

}

void XdgView::move_to_front()
{

}

void XdgView::move_to_back()
{

}

View *XdgView::get_root()
{
    struct wlr_xdg_toplevel *root = top_parent_of_view();
    struct wlr_xdg_surface *surface = (struct wlr_xdg_surface *)root->base;
    return (View *)surface->data;
}

void XdgView::append_children(wl_array *children)
{

}

bool XdgView::is_related(wlr_surface *surface)
{

    return false;
}

view_size_hints XdgView::get_size_hints()
{
    return {};
}

view_wants_focus XdgView::wants_focus()
{
    return {};
}

bool XdgView::has_strut_partial()
{
    return false;
}

wlr_surface *XdgView::surface_at(double vx, double vy, double *sx, double *sy)
{
    return wlr_xdg_surface_surface_at(m_xdg_surface, vx, vy, sx, sy);
}

wlr_box XdgView::get_surface_geometry()
{
    struct wlr_box box;
    wlr_xdg_surface_get_geometry(m_xdg_surface, &box);
    return box;
}

void XdgView::resize(wlr_box geo)
{
    struct wlr_box border = m_base->get_surface_geometry();
    wlr_xdg_toplevel_set_size(m_xdg_surface->toplevel,
                              geo.width - 2 * border.x,
                              geo.height - 2 * border.y);
}

void XdgView::focus()
{

}

bool XdgView::is_toplevel()
{
    return m_xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL;
}

void XdgView::init_foreign_toplevel()
{
#if 0
    foreign_toplevel_handle_create();
    struct wlr_xdg_toplevel *toplevel = m_xdg_surface->toplevel;
    if (!toplevel->parent) {
        return;
    }
    struct wlr_xdg_surface *surface = toplevel->parent->base;
    struct view *parent = surface->data;
    if (!parent->toplevel.handle) {
        return;
    }
    wlr_foreign_toplevel_handle_v1_set_parent(view->toplevel.handle, parent->toplevel.handle);
#endif
}

void XdgView::commit()
{
#if 0
    // assert(view->surface);

    struct wlr_box size;
    wlr_xdg_surface_get_geometry(m_xdg_surface, &size);

    /*
     * Qt applications occasionally fail to call set_window_geometry
     * after a configure request, but do correctly update the actual
     * surface extent. This results in a mismatch between the window
     * decorations (which follow the logical geometry) and the visual
     * size of the client area. As a workaround, we try to detect
     * this case and ignore the out-of-date window geometry.
     */
    if (size.width != view->pending.width
        || size.height != view->pending.height) {
        struct wlr_box extent;
        wlr_surface_get_extends(xdg_surface->surface, &extent);
        if (extent.width == view->pending.width
            && extent.height == view->pending.height) {
            wlr_log(WLR_DEBUG, "window geometry for client (%s) "
                               "appears to be incorrect - ignoring",
                    view_get_string_prop(view, "app_id"));
            size = extent; /* Use surface extent instead */
        }
    }

    struct wlr_box *current = &view->current;
    bool update_required = current->width != size.width
                           || current->height != size.height;

    uint32_t serial = view->pending_configure_serial;
    if (serial > 0 && serial == xdg_surface->current.configure_serial) {
        assert(view->pending_configure_timeout);
        wl_event_source_remove(view->pending_configure_timeout);
        view->pending_configure_serial = 0;
        view->pending_configure_timeout = NULL;
        update_required = true;
    }

    if (update_required) {
        view_impl_apply_geometry(view, size.width, size.height);

        /*
         * Some views (e.g., terminals that scale as multiples of rows
         * and columns, or windows that impose a fixed aspect ratio),
         * may respond to a resize but alter the width or height. When
         * this happens, view->pending will be out of sync with the
         * actual geometry (size *and* position, depending on the edge
         * from which the resize was attempted). When no other
         * configure is pending, re-sync the pending geometry with the
         * actual view.
         */
        if (!view->pending_configure_serial) {
            view->pending = view->current;

            /*
             * wlroots retains the size set by any call to
             * wlr_xdg_toplevel_set_size and will send the retained
             * values with every subsequent configure request. If a
             * client has resized itself in the meantime, a
             * configure request that sends the now-outated size
             * may prompt the client to resize itself unexpectedly.
             *
             * Calling wlr_xdg_toplevel_set_size to update the
             * value held by wlroots is undesirable here, because
             * that will trigger another configure event and we
             * don't want to get stuck in a request-response loop.
             * Instead, just manipulate the dimensions that *would*
             * be adjusted by the call, so the right values will
             * apply next time.
             *
             * This is not ideal, but it is the cleanest option.
             */
            struct wlr_xdg_toplevel *toplevel =
                xdg_toplevel_from_base(view);
            toplevel->scheduled.width = view->current.width;
            toplevel->scheduled.height = view->current.height;
        }
    }
#endif
}

struct wlr_xdg_toplevel *XdgView::top_parent_of_view()
{
    struct wlr_xdg_toplevel* toplevel = m_xdg_surface->toplevel;
    while (toplevel->parent) {
        toplevel = toplevel->parent;
    }
    return toplevel;
}

void XdgView::destroy()
{
    m_base->destroy();
}

void XdgView::request_move()
{
    sig_toplevel_request_move.emit(this);
}

void XdgView::request_resize(struct wlr_xdg_toplevel_resize_event *event)
{
    sig_toplevel_request_resize.emit(this, (wlr_edges)event->edges);
}

void XdgView::request_maximize()
{

}

void XdgView::request_minimize()
{

}

void XdgView::request_fullscreen()
{

}

void XdgView::set_title()
{

}

void XdgView::set_app_id()
{

}

void XdgView::new_popup()
{

}
